/*
    TunesRemote+ - http://code.google.com/p/tunesremote-plus/
    
    Copyright (C) 2008 Jeffrey Sharkey, http://jsharkey.org/
    Copyright (C) 2010 TunesRemote+, http://code.google.com/p/tunesremote-plus/
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    The Initial Developer of the Original Code is Jeffrey Sharkey.
    Portions created by Jeffrey Sharkey are
    Copyright (C) 2008. Jeffrey Sharkey, http://jsharkey.org/
    All Rights Reserved.
 */

package org.tunesremote;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.*;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.*;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;
import org.tunesremote.daap.Library;
import org.tunesremote.daap.RequestHelper;
import org.tunesremote.daap.Response;
import org.tunesremote.daap.Session;
import org.tunesremote.util.ThreadExecutor;

import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.List;

public class AlbumsActivity extends BaseBrowseActivity {

	public final static String TAG = AlbumsActivity.class.toString();

	protected BackendService backend;
	protected Session session;
	protected Library library;
	protected ListView list;
	protected AlbumsAdapter adapter;
	protected String artist;
	protected Bitmap blank;
	protected Animation fadeUp;

	protected int imageSize = 55;

	public ServiceConnection connection = new ServiceConnection() {
		public void onServiceConnected(ComponentName className,
		                               final IBinder service) {
			ThreadExecutor.runTask(new Runnable() {

				public void run() {
					try {
						backend = ((BackendService.BackendBinder) service)
								.getService();
						session = backend.getSession();

						if (session == null)
							return;

						adapter.results.clear();

						// begin search now that we have a backend
						library = new Library(session);
						library.readAlbums(adapter, artist);
					} catch (Exception e) {
						Log.e(TAG, "onServiceConnected:" + e.getMessage());
					}
				}

			});

		}

		public void onServiceDisconnected(ComponentName className) {
			backend = null;
			session = null;

		}
	};

	public Handler resultsUpdated = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			adapter.notifyDataSetChanged();
		}
	};

	@Override
	public void onStart() {
		super.onStart();
		this.bindService(new Intent(this, BackendService.class), connection,
				Context.BIND_AUTO_CREATE);

	}

	@Override
	public void onStop() {
		super.onStop();
		this.unbindService(connection);

	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.gen_list);

		this.artist = this.getIntent().getStringExtra(Intent.EXTRA_TITLE);
		this.list = this.getListView();
		this.adapter = new AlbumsAdapter(this);
		this.setListAdapter(adapter);

		((TextView) this.findViewById(android.R.id.empty))
				.setText(R.string.albums_empty);

		DisplayMetrics outMetrics = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
		imageSize = outMetrics.densityDpi * 55;
		if (imageSize > 110) {
			imageSize = 110;
		}

		fadeUp = AnimationUtils.loadAnimation(this, R.anim.fade_up);

		this.registerForContextMenu(this.getListView());

		this.getListView().setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view,
			                        int position, long id) {
				try {
					// launch activity to browse track details for this albums
					if (position == 0) {
						Intent intent = new Intent(AlbumsActivity.this,
								TracksActivity.class);
						intent.putExtra(Intent.EXTRA_TITLE, "");
						intent.putExtra("Artist", AlbumsActivity.this.artist);
						intent.putExtra("AllAlbums", true);
						AlbumsActivity.this.startActivityForResult(intent, 1);
					} else {
						final Response resp = (Response) adapter
								.getItem(position);
						final String albumid = resp.getNumberString("mper");

						Intent intent = new Intent(AlbumsActivity.this,
								TracksActivity.class);
						intent.putExtra(Intent.EXTRA_TITLE, albumid);
						intent.putExtra("minm", resp.getString("minm"));
						intent.putExtra("miid",
								Long.valueOf(resp.getNumberLong("miid"))
										.intValue());
						intent.putExtra("Artist", AlbumsActivity.this.artist);
						intent.putExtra("AllAlbums", false);
						AlbumsActivity.this.startActivityForResult(intent, 1);
					}
				} catch (Exception e) {
					Log.w(TAG, "onCreate:" + e.getMessage());
				}

			}
		});

	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
	                                ContextMenu.ContextMenuInfo menuInfo) {

		final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;

		try {
			if (info.position > 0) {
				// create context menu to play entire artist
				final Response resp = (Response) adapter.getItem(info.position);
				menu.setHeaderTitle(resp.getString("minm"));
				final String albumid = resp.getNumberString("mper");

				final MenuItem play = menu.add(R.string.albums_menu_play);
				play.setOnMenuItemClickListener(new OnMenuItemClickListener() {
					public boolean onMenuItemClick(MenuItem item) {
						session.controlPlayAlbum(albumid, 0);
						AlbumsActivity.this.setResult(RESULT_OK, new Intent());
						AlbumsActivity.this.finish();
						return true;
					}
				});

				final MenuItem queue = menu.add(R.string.albums_menu_queue);
				queue.setOnMenuItemClickListener(new OnMenuItemClickListener() {
					public boolean onMenuItemClick(MenuItem item) {
						session.controlQueueAlbum(albumid);
						AlbumsActivity.this.setResult(RESULT_OK, new Intent());
						AlbumsActivity.this.finish();
						return true;
					}
				});

				final MenuItem browse = menu.add(R.string.albums_menu_browse);
				browse.setOnMenuItemClickListener(new OnMenuItemClickListener() {
					public boolean onMenuItemClick(MenuItem item) {
						Intent intent = new Intent(AlbumsActivity.this,
								TracksActivity.class);
						intent.putExtra(Intent.EXTRA_TITLE, albumid);
						intent.putExtra("Artist", AlbumsActivity.this.artist);
						intent.putExtra("AllAlbums", false);
						AlbumsActivity.this.startActivityForResult(intent, 1);

						return true;
					}

				});
			}
		} catch (Exception e) {
			Log.w(TAG, "onCreateContextMenu:" + e.getMessage());
		}

	}

	public class AlbumsAdapter extends BaseAdapter implements TagListener {

		protected Context context;
		protected LayoutInflater inflater;
		protected final List<Response> results = new LinkedList<Response>();

		public AlbumsAdapter(Context context) {
			this.context = context;
			this.inflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

		}

		public void foundTag(String tag, final Response resp) {
			if (resp == null) {
				return;
			}

			runOnUiThread(new Runnable() {

				public void run() {
					// add a found search result to our list
					if (resp.containsKey("minm"))
						results.add(resp);
				}

			});
		}

		public void searchDone() {
			resultsUpdated.removeMessages(-1);
			resultsUpdated.sendEmptyMessage(-1);
		}

		public Object getItem(int position) {
			if (position == 0)
				return null;
			else
				return results.get(position - 1);
		}

		@Override
		public boolean hasStableIds() {
			return true;
		}

		public int getCount() {
			return results.size() + 1;
		}

		public long getItemId(int position) {
			return position;
		}

		public View getView(int position, View convertView, ViewGroup parent) {

			try {
				if (position == 0) {
					// if (convertView == null)
					convertView = inflater.inflate(R.layout.item_artist,
							parent, false);
					((TextView) convertView.findViewById(android.R.id.text1))
							.setText("All");
				} else {
					// if (convertView == null)
					convertView = this.inflater.inflate(R.layout.item_album,
							parent, false);
					Response child = (Response) this.getItem(position);
					String title = child.getString("minm");
					String caption = AlbumsActivity.this.getResources()
							.getString(R.string.albums_album_caption,
									child.getNumberLong("mimc"));

					((TextView) convertView.findViewById(android.R.id.text1))
							.setText(title);
					((TextView) convertView.findViewById(android.R.id.text2))
							.setText(caption);

					// go load image art
					((ImageView) convertView.findViewById(android.R.id.icon))
							.setImageBitmap(blank);
					new LoadPhotoTask().execute(Integer.valueOf(position),
							Integer.valueOf((int) child.getNumberLong("miid")));
				}
			} catch (Exception e) {
				Log.w(TAG, "getView:" + e.getMessage());
			}

			return convertView;

		}

	}

	protected SparseArray<SoftReference<Bitmap>> memcache = new SparseArray<SoftReference<Bitmap>>();

	private class LoadPhotoTask extends AsyncTask<Object, Void, Object[]> {
		@Override
		public Object[] doInBackground(Object... params) {

			Integer position = (Integer) params[0];
			Integer itemid = (Integer) params[1];

			Bitmap bitmap = null;
			try {

				// first check if we have an in-memory cache of this bitmap
				if (memcache.get(itemid) != null) {
					bitmap = memcache.get(itemid).get();
				}

				if (bitmap != null) {
					Log.d(TAG,
							String.format("MEMORY cache hit for %s",
									itemid.toString()));
				} else {

					// fetch the album cover from itunes
					byte[] raw = RequestHelper.request(String.format(
							"%s/databases/%d/groups/%d/extra_data/artwork?session-id=%s&mw="
									+ imageSize + "&mh=" + imageSize
									+ "&group-type=albums",
							session.getRequestBase(), session.databaseId,
							itemid, session.sessionId), false);
					bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length);

					// if SOMEHOW (404, etc) this image was still null, then
					// save as
					// blank
					if (bitmap == null)
						bitmap = blank;

					// try removing any stale references
					memcache.remove(itemid);
					memcache.put(itemid, new SoftReference<Bitmap>(bitmap));
				}
			} catch (Exception e) {
				Log.w(TAG, "LoadPhotoTask:" + e.getMessage());
			}

			return new Object[]{position, bitmap};
		}

		@Override
		protected void onPostExecute(Object[] result) {
			try {
				if (result == null) {
					return;
				}
				// update gui to show the newly-fetched albumart
				final int position = ((Integer) result[0]).intValue();
				final Bitmap bitmap = (Bitmap) result[1];

				// skip if bitmap wasnt found
				if (bitmap == null)
					return;

				// skip updating this item if outside of bounds
				if (position < list.getFirstVisiblePosition()
						|| position > list.getLastVisiblePosition())
					return;

				// find actual position and update view
				int visible = position - list.getFirstVisiblePosition();
				View view = list.getChildAt(visible);
				ImageView v = (ImageView) view.findViewById(android.R.id.icon);
				v.setVisibility(View.INVISIBLE);
				v.setImageBitmap(bitmap);
				v.startAnimation(AnimationUtils.loadAnimation(
						AlbumsActivity.this, R.anim.fade_up));

			} catch (Exception e) {
				// we probably ran into an item thats now collapsed, just ignore
				Log.d(TAG, "end:" + e.getMessage());
			}
		}
	}

}
